#include <iostream>
#include <pthread.h>
#include <mutex>
#include <queue>
#include <unistd.h>
#include "lockGurad.hpp"

const int DEFAULT_CAP = 5;

template<class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return _bq.size() == 0;
    }
    bool isQueueFill()
    {
        return _bq.size() == _capacity;
    }

public:
    BlockQueue(const int& capacity = DEFAULT_CAP)
    :_capacity(capacity)
    {
        pthread_mutex_init(&_mtx,nullptr);
        pthread_cond_init(&_isFillCond,nullptr);
        pthread_cond_init(&_isEmptyCond,nullptr);
    }

    //push进阻塞队列中，其实就是生产者生产东西放进去
    void Push(const T& in)
    {
        lockGurad lg(&_mtx);
        //1.先判断是否符合条件
        while(isQueueFill())
            //第一个参数代表在xx条件下等待
            //pthread_cond_wait中的第二个参数是一个锁，当wait调用成功后，传入的锁会进行自动释放
            //当pthread_cond_wait唤醒时也会自动加锁
            pthread_cond_wait(&_isFillCond,&_mtx);
        //2.访问临界资源
        _bq.push(in);
        //控制生产者的生产情况
        // if(_bq.size() == _capacity) pthread_cond_signal(&_isEmptyCond); 
        //pthread_mutex_unlock(&_mtx);
        pthread_cond_signal(&_isEmptyCond);
    }

    //从阻塞队列中pop出来，其实就是消费者消费东西的过程
    void Pop(T* out)
    {
        lockGurad lg(&_mtx);
        while(isQueueEmpty())
            pthread_cond_wait(&_isEmptyCond,&_mtx);
        //2.访问临界资源
        *out = _bq.front();
        _bq.pop();
        //控制消费者的消费情况
        // if(_bq.size() == 0) pthread_cond_signal(&_isFillCond);
        //pthread_mutex_unlock(&_mtx);
        //3.唤醒生产者，具有原子性，所以可以在锁外进行
        pthread_cond_signal(&_isFillCond);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_isEmptyCond);
        pthread_cond_destroy(&_isFillCond);
    }
private:
    std::queue<T> _bq; //阻塞队列
    int _capacity; //容量上限
    pthread_mutex_t _mtx; //互斥锁
    //条件变量，用于线程同步
    pthread_cond_t _isFillCond;  //用于表示阻塞队列是否满
    pthread_cond_t _isEmptyCond; //用于表示是否为空
};

